/***********************************************************************

	This file is part of KEEL-software, the Data Mining tool for regression, 
	classification, clustering, pattern mining and so on.

	Copyright (C) 2004-2010
	
	F. Herrera (herrera@decsai.ugr.es)
    L. Sánchez (luciano@uniovi.es)
    J. Alcalá-Fdez (jalcala@decsai.ugr.es)
    S. García (sglopez@ujaen.es)
    A. Fernández (alberto.fernandez@ujaen.es)
    J. Luengo (julianlm@decsai.ugr.es)

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see http://www.gnu.org/licenses/
  
**********************************************************************/



package keel.Algorithms.Subgroup_Discovery.MESDIF.MESDIF;

import org.core.*;

/**
 * <p>Methods to define the genetic algorithm and to apply operators and reproduction schema
     * 
 * @author Writed by Pedro González (University of Jaen) 22/08/2004
 * @author Modified by Pedro González (University of Jaen) 4/08/2007
 * @author Modified by Cristóbal J. Carmona (University of Jaen) 30/06/2010
 * @version 2.0
 * @since JDK1.5
 * </p>
 */
public class Genetic {
    /**
     * <p>
     * Methods to define the genetic algorithm and to apply operators and reproduction schema
     * </p>
     */

    private Population poblac;          // Main Population
    private int long_poblacion;         // Number of individuals of the population

    private Population elite;           /* Elite Set */
    private int long_elite;             // Number of individuals of the elite population
    private int used_elite;             /* Number of different individuals in the final elite set */

    private Population temporal;        /* Temporal population (max elite plus poblac) to compute fitness */
    private int used_temp;              /* Number of individuals in the temporal population */

    private Population Inter;           // Local Inter population generated by applying genetic ops
    private Population Des;             // Local Population generated by cross and reproduction ops
    private int descendientes;          // Number of descendants generated in the iteration of the GA

    private float cross_prob;           // Cross probability
    private float mut_prob;             // Mutation probability
    private int Mu_next;                // Next gen to mutate
    private float min_conf;             // Minimun value for the confidence

    private int Gen;                    // Number of generations performed by the GA
    private int n_eval;                 // Maximun number of evaluations per ejecution
    private int Trials;                 // Number of chromosomes evaluated so far

    private String RulesRep = "CAN";    // Rules representation

    private String Obj[];               // Names of the quality measures used as objectives


    
    /**
     * <p>
     * Creates a new instance of Genetic
     * </p>
     */
    public Genetic() {
    }
    
    
    /**
     * <p>
     * JoinTemp
     * <p>
     * Joins Elite population with Population into temporal population
     * Previous contents are lost
     */
    public void JoinTemp() {

	/* First we copy elite population individuals in temporal population */
	for (int i=0;i<long_elite;i++) {
            temporal.copyIndiv(i, elite.getIndiv(i));
	}

	/* Set the number of individuals in temporal */
	used_temp = used_elite;

	/* Now we add "poblacion" population individuals to "temporal" population*/
	for (int i=0;i<long_poblacion;i++) {
            temporal.copyIndiv(used_temp+i, poblac.getIndiv(i));
	}
    // Update number of individuals in temporal
	used_temp += long_poblacion;

    }


    /**
     * <p>
     * CompletaElite
     * <p>
     * Stores in elite the better individuals of temporal
     *    It is suposed here there that are at last the number of individulas that fit on elite
     */
    public void CompletaElite () {
        int i,j, best;
        int posicion[]= new int[used_temp];
        int pos[]= new int[used_temp];
        float perf;

        // First sort the individuals of temporal by their fitness
        /* "Posicion" used to store the ordered position of the individual in the population */
        for (i=0;i<used_temp;i++) {
            posicion[i] = -1; /* Not really necesary */
        }
        /* Compute the ranking of each individual of the population: elemento 0 is the better */
        for (i=0;i<used_temp;i++) {
            /* Find better individual of the rest */
            best=-1;
            perf=0;
            for (j=0;j<used_temp;j++) {
                if (posicion[j]==-1 && (best==-1 || temporal.getIndivFitness(j)< perf)) {
                    perf = temporal.getIndivFitness(j);
                    best = j;
                }
            }
            /* Mark the individual with the ranking */
            posicion[best] = i;
        }

        // Next invert to obtain the selection order of the individuals
        for (int f=0; f<pos.length; f++)
            pos[posicion[f]]=f;

        // Now they are copied in fitness order until the number of elements of elite
        for (i=0;i<long_elite;i++) {
            elite.copyIndiv(i, temporal.getIndiv(pos[i]));
        }

        // Finally, the number of elements in size is "long_elite" again
        used_elite = long_elite;

    }


    /**
     * <p>
     * TruncaElite
     * <p>
     * Stores in elite part of the non-dominated individuals of temporal
     *    This is because temporal contains more non-dominated that fits in elite
     */
    public void TruncaElite () {
    	int numInter=0;
    	double MAXDOUBLE = 99999999;  //A large number used for sorting

        // Copy non-dominated individuals of temporal in Inter
        for (int x=0;x<used_temp;x++) {
            if (!temporal.getIndivDom(x)) {
                Inter.copyIndiv(numInter, temporal.getIndiv(x));
                numInter++;
            }
        }

        // Create arrays
        double[][] distances = new double[numInter][numInter];  // Array for the distances
        int[][] sortedIndex = new int[numInter][numInter];      // Array of indexes to individuals sorted by distances
        boolean[] mantener = new boolean[numInter];             // Array for the individuals to keep in elite
        for (int x=0;x<numInter;x++)
            mantener[x] = true;

        // Set distances between elites
        for ( int y=0; y<numInter; y++ ) {
            for(int z=y+1;z<numInter;z++) {
                distances[y][z] = Inter.getIndiv(y).calcDist(Inter.getIndiv(z));
                distances[z][y] = distances[y][z];
            }
            distances[y][y] = -1;
        }

        // Create sorted index lists
        for (int i=0; i<numInter; i++) {
            sortedIndex[i][0] = 0;
            for (int j=1; j<numInter; j++) { // for all columns
                int k = j;  // insertion position
                while (k>0 && distances[i][j] < distances[i][sortedIndex[i][k-1]]) {
                    sortedIndex[i][k] = sortedIndex[i][k-1];
                    k--;
                }
                sortedIndex[i][k] = j;
            }
        }

        // Now we compute into "mantener" the "long_elite" individuals of Inter to keep in elite
        int mf = numInter;
        while (mf> long_elite) {
            // search for minimal distances
            int minpos = 0;
            for (int i=1; i<numInter; i++) {
                for (int j=1; j<mf; j++) {
                    if (distances[i][sortedIndex[i][j]] < distances[minpos][sortedIndex[minpos][j]]) {
                        minpos = i;
                        break;
                    }
                    else if (distances[i][sortedIndex[i][j]] > distances[minpos][sortedIndex[minpos][j]])
                        break;
                }
            }
            // kill entries of pos (which is now minpos) from lists
            for (int i=0; i<numInter; i++) {
                // Don't choose these positions again
                distances[i][minpos] = MAXDOUBLE;
                distances[minpos][i] = MAXDOUBLE;
                for (int j=1; j<mf-1; j++) {
                    if (sortedIndex[i][j]==minpos) {
                        sortedIndex[i][j] = sortedIndex[i][j+1];
                        sortedIndex[i][j+1] = minpos;
                    }
                }
            }
            mantener[minpos] = false;
            mf--;
        }

        // Finally, copy the rest of individuals of Inter in elite
        int pos=0;
        for (int i=0; i<numInter;i++) {
            if (mantener[i]) {
                elite.copyIndiv(pos, Inter.getIndiv(i));
                pos++;
            }
        }

        // Finally, the number of elements in size is "long_elite" again
        used_elite = long_elite;

    }


    /**
     * <p>
     * Methods to get the lenght of the population
     * </p>
     * @return              Length of the population
     */
    public int getLenghtPop () {
        return long_poblacion;
    }

    /**
     * <p>
     * Methods to set the lenght of the population
     * </p>
     * @param value              Length of the population
     */
    public void setLenghtPop (int value) {
        long_poblacion = value;    }



    /**
     * <p>
     * Methods to get the lenght of the elite population
     * </p>
     * @return              Length of the elite population
     */
    public int getLenghtElite () {
        return long_elite;
    }

    /**
     * <p>
     * Methods to set the lenght of the elite population
     * </p>
     * @param value              Length of the elite population
     */
    public void setLenghtElite (int value) {
        long_elite = value;    }



    /**
     * <p>
     * Method to get the number of different individuals in the final elite population
     * </p>
     * @return              Size of the final elite population
     */
    public int getUsedElite () {
        return used_elite;
    }

    /**
     * <p>
     * Method to set the number of indivisuals in the final elite population
     * </p>
     */
    //////////////// no utilizar fuera de genetico
    private void setUsedElite (int value) {
        used_elite = value;    }



    /**
     * <p>
     * Sets the value for indicating the position of the next mutation
     * </p>
     * @param value             Value of the variable Mu_next
     */
    public void setMuNext (int value) {
        Mu_next = value;
    }
        
    
    /**
     * <p>
     * Methods to get the number of evaluation to perform in an iteration of the GA
     * </p>
     * @return                  Number of evaluations
     */
    public int getNEval () {
        return n_eval;    }


    /**
     * <p>
     * Methods to set the number of evaluation to perform in an iteration of the GA
     * </p>
     * @param value             Number of evaluations
     */
    public void setNEval (int value) {
        n_eval = value;    }

    
    /**
     * <p>
     * Methods to get the value for the minimum confidence of the rules to be generated
     * </p>
     * @return                  Minimum confidence
     */
    public float getMinConf () {
        return min_conf;    }


    /**
     * <p>
     * Methods to set the value for the minimum confidence of the rules to be generated
     * </p>
     * @param value             Minimum confidence
     */
    public void setMinConf (float value) {
        min_conf = value;    }

    /**
     * <p>
     * Gets the representation of the rules
     * </p>
     * @return              Representation of the rules
     */
    public String getRulesRep(){
        return RulesRep;
    }

    /**
     * <p>
     * Sets the representation of the rules
     * </p>
     * @param value              Representation of the rules
     */
    public void setRulesRep(String value){
        RulesRep = value;
    }

    /**
     * <p>
     * Methods to get the value for the crossover probability
     * </p>
     * @return                  Crossover probability
     */
    public float getProbCross () {
        return cross_prob;
    }

    /**
     * <p>
     * Methods to set the value for the crossover probability
     * </p>
     * @param value             Crossover probability
     */
    public void setProbCross (float value) {
        cross_prob = value;
    }

    /**
     * <p>
     * Methods to get the value for the mutation probability
     * </p>
     * @return                  Mutation probability
     */
    public float getProbMut () {
        return mut_prob;
    }

    /**
     * <p>
     * Methods to set the value for the mutation probability
     * </p>
     * @param value             Mutation probability
     */
    public void setProbMut (float value) {
        mut_prob= value;
    }


    /**
     * <p>
     * Method to create the Obj array with num (+1) elements
     * </p>
     * @param num           Num of elementos
     */
    public void  createObj (int num) {
        Obj = new String[num+1];
    }


    /**
     * <p>
     * Method to get the name of the quality measure in position pos
     * </p>
     * @param pos           Position of the quality measure
     * @return              Nama of the quality measure in position pos
     */
    public String getObj (int pos) {
        return Obj[pos];
    }


    /**
     * <p>
     * Method to set the name of the quality measure in position pos
     * </p>
     * @param pos               Postion of the quality measure
     * @param value             Name of the quality measure in position pos
     */
    public void setObj (int pos, String value) {
        Obj[pos] = value;
    }


    /**
     * <p>
     * Get the measurements of a single rule of the main population
     * </p>
     * @param pos           Position of the individual
     * @param nFile         Name of the file to write the values
     * @return  the measures of a single rule of the main population 
     */
    public QualityMeasures getQualityMeasures (int pos, String nFile) {
        return poblac.getMedidas(this, pos, nFile);
    }

    /**
     * <p>
     * Get the measurements of a single rule of the elite population
     * </p>
     * @param pos           Position of the individual
     * @param nFile         Name of the file to write the values
     * @return  the measurements of a single rule of the elite population
     */
    public QualityMeasures getQualityMeasuresElite (int pos, String nFile) {
        return elite.getMedidas(this, pos, nFile);
    }


    /**
     * <p>
     * Evaluates an individual of the main population
     * </p>
     * @param pos           Position of the invidivual
     * @param AG            Genetic algorithm object
     * @param Variables     Structure of the Variables
     * @param Examples      Structure of the Examples
     */
    public void evalIndiv (int pos, Genetic AG, TableVar Variables, TableDat Examples) {
        poblac.evalIndiv(pos, AG, Variables, Examples);
    }


    /**
     * <p>
     * Evaluates an individual of the elite population
     * </p>
     * @param pos           Position of the invidivual
     * @param AG            Genetic algorithm object
     * @param Variables     Structure of the Variables
     * @param Examples      Structure of the Examples
     */
    public void evalEliteIndiv (int pos, Genetic AG, TableVar Variables, TableDat Examples) {
        elite.evalIndiv(pos, AG, Variables, Examples);
    }



    /**
     * <p>
     * Returns de hole chromosome of the selected individual of the main population
     * </p>
     * @param pos           Position of the individual
     * @return              Canonical chromosome of the individual
     */
    public CromCAN getIndivCromCAN (int pos) {
        return poblac.getIndivCromCAN(pos);
    }

    /**
     * <p>
     * Returns de hole chromosome of the selected individual of the main population
     * </p>
     * @param pos           Position of the individual
     * @return              DNF chromosome of the individual
     */
    public CromDNF getIndivCromDNF (int pos) {
        return poblac.getIndivCromDNF(pos);
    }


    /**
     * <p>
     * Returns de hole chromosome of the selected individual of the elite population
     * </p>
     * @param pos           Position of the individual
     * @return              Canonical chromosome of the individual
     */
    public CromCAN getEliteIndivCromCAN (int pos) {
        return elite.getIndivCromCAN(pos);
    }

    /**
     * <p>
     * Returns de hole chromosome of the selected individual iof the elite pupulation
     * </p>
     * @param pos           Position of the individual
     * @return              DNF chromosome of the individual
     */
    public CromDNF getEliteIndivCromDNF (int pos) {
        return elite.getIndivCromDNF(pos);
    }



    /**
     * <p>
     * BinTournSelect
     * Applies the selection schema of the genetic algorithm
     * </p>
     * Binary tournament selection from elite to inter
     */
    public void BinTournSelect() {
        int winner;

        /* Uses a binary tournament selection to select Globals.Param.long_poblacion individuals and stores on population inter */
        for (int i=0;i<long_poblacion;i++) {

            /* Ramdon selection of the individuals */
            int opponent1 = Randomize.Randint (0,long_elite-1);
            int opponent2 = Randomize.Randint (0,long_elite-1);

            winner = opponent1;  // Initially, the first is the winner
            /* If the second individual is better than the first, it is selected as winner */
            if ( elite.getIndivFitness(opponent2) < elite.getIndivFitness(opponent1) )
                winner = opponent2;
            else if (elite.getIndivFitness(opponent2) == elite.getIndivFitness(opponent1) ) {
                /* Will have to check the distances. By now, select the first */
                winner = opponent1;
            }

            /* Copies the winner in the correspondant position of Inter population */
            Inter.copyIndiv( i, elite.getIndiv(winner));
        }


    }


    /**
     * <p>
     * Multipoint cross operator for the genetic algorithm
     * </p>
     * Better chromosomes of Inter are at the first positions
     *    as the cross individual, stored in Des
     * 
     * @param Variables         Structure of Variables
     * @return              Number of crossovers performed
     */
    
    public int MultipointCrossover (TableVar Variables) {
        int mom, dad, xpoint1, xpoint2;
        int numcruces = (int) Math.round(cross_prob*long_poblacion/2);

        for (int x=0; x<numcruces; x++) {
            mom = Randomize.Randint (0,long_poblacion-1);
            dad = Randomize.Randint (0,long_poblacion-1);

            /* Copy the individuals to cross */
            if (RulesRep.equalsIgnoreCase("CAN"))   // Canonical representation of the rules
                for (int i=0; i<Variables.getNVars(); i++)
                {
                    Des.setCromElem (2*x,   i, Inter.getCromElem(mom,i));
                    Des.setCromElem (2*x+1, i, Inter.getCromElem(dad,i));
                }
            else   // DNF representation of the rules
                for (int i=0; i<Variables.getNVars(); i++)
                    for (int j=0; j<=Variables.getNLabelVar(i);j++)
                    {
                        Des.setCromElemGene (2*x,   i,j, Inter.getCromElemGene(mom,i,j));
                        Des.setCromElemGene (2*x+1, i,j, Inter.getCromElemGene(dad,i,j));
                    }
            
            /* Generation of the two point of cross */
            xpoint1 = Randomize.Randint (0,(Variables.getNVars()-1));
            if (xpoint1!=Variables.getNVars()-1)
                xpoint2 = Randomize.Randint ((xpoint1+1),(Variables.getNVars()-1));
            else
                xpoint2 = Variables.getNVars()-1;

            /* Cross the parts between both points  */
            if (RulesRep.equalsIgnoreCase("CAN"))   // Canonical representation of the rules
                for (int i=xpoint1;i<=xpoint2;i++) 
                {
                    Des.setCromElem(2*x,   i, Inter.getCromElem(dad,i));
                    Des.setCromElem(2*x+1, i, Inter.getCromElem(mom,i));
                }
            else   // DNF representation of the rules
                for (int i=xpoint1;i<=xpoint2;i++)
                    for (int j=0; j<=Variables.getNLabelVar(i);j++)
                    {
                        Des.setCromElemGene (2*x,   i,j, Inter.getCromElemGene(dad,i,j));
                        Des.setCromElemGene (2*x+1, i,j, Inter.getCromElemGene(mom,i,j));
                    }

            /* New individuals are not evaluated */
            Des.setIndivEvaluated (2*x, false);
            Des.setIndivEvaluated (2*x+1, false);

        }
      return (numcruces);   /* Number of crossovers */

    }
        

     /**
     * <p>
     * Applies the mutation operator. Uniform biased operator
     * </p>
     * @param Variables             Structure of Variables
     */
    public void UniformBiasedMutation (TableVar Variables) {
        int i, j;
        int posiciones = Variables.getNVars()*long_poblacion;
        
        if (mut_prob > 0)
            while (Mu_next < posiciones) {
                /* Determine the chromosome and gene to be muted */
                i = Mu_next/Variables.getNVars();
                j = Mu_next%Variables.getNVars();

                /* Copy the chromosome */
                if (RulesRep.equalsIgnoreCase("CAN"))   // Canonical representation of the rules
                    for (int h=0; h<Variables.getNVars(); h++)
                        Des.setCromElem(descendientes, h, Inter.getCromElem(i,h));
                else   // DNF representation of the rules
                    for (int h=0; h<Variables.getNVars(); h++)
                        for (int l=0; l<=Variables.getNLabelVar(h);l++)
                            Des.setCromElemGene (descendientes, h, l, Inter.getCromElemGene(i,h,l));
        
                /* Make the mutation */
                /* Half the mutations eliminates the variable from the rule */
                /* The others sets a random value (including the elimination) */
                if (Randomize.Randint (0,10) <=5) {  // Eliminate
                    if (RulesRep.equalsIgnoreCase("CAN"))   // Canonical representation of the rules
                    {
                        if (!Variables.getContinuous(j))
                            Des.setCromElem(descendientes, j, (int)Variables.getMax(j)+1);
                        else
                            Des.setCromElem(descendientes, j, Variables.getNLabelVar(j));
                    }
                    else   // DNF Representation
                    {
                        for (int l=0; l<=Variables.getNLabelVar(j); l++)
                            Des.setCromElemGene(descendientes, j, l, 0);
                    }
                    
                }
                else {   // Mutate
                    if (RulesRep.equalsIgnoreCase("CAN"))   // Canonical representation of the rules
                    {
                    if (!Variables.getContinuous(j))
                        Des.setCromElem(descendientes, j, Randomize.Randint((int)Variables.getMin(j), (int)Variables.getMax(j)));
                    else
                        Des.setCromElem(descendientes, j, Randomize.Randint(0,(Variables.getNLabelVar(j)-1)));
                    }
                    else   // DNF representation
                    {
                        int interv=0;
                        for (int l=0; l<Variables.getNLabelVar(j); l++) {
                            // pone a todos valores aleatorios, y calcula si interviene
                            if (Randomize.Rand()<0.5)
                                Des.setCromElemGene (descendientes, j, l, 1);
                            else
                                Des.setCromElemGene (descendientes, j, l, 0);
                            // cambiar después de las comprobaciones por:
                            // Des.setCromElemGene(descendientes, j, l, Randomize.Randint(0,1));
                            if (Des.getCromElemGene(descendientes, j, l)==1)
                                interv ++;
                        }
                        // si no interviene ningún valor o intervienen todos, la variable no interviene
                        if (interv==0 || interv==Variables.getNLabelVar(j))
                            Des.setCromElemGene(descendientes, j, Variables.getNLabelVar(j), 0);
                        else
                            Des.setCromElemGene(descendientes, j, Variables.getNLabelVar(j), 1);
                    }
                        
                }
                descendientes++;

                /* Marks the chromosome as not evaluated */
                Des.setIndivEvaluated(i,false);

                /* Compute next position to be muted */
                if (mut_prob<1) {
                    float  m = (float) Randomize.Rand();
                    Mu_next += Math.ceil (Math.log(m) / Math.log(1.0 - mut_prob));
                }   
                else {
                    Mu_next += 1;
                }

            }

        Mu_next -= posiciones;
            
    }

    
    /**
     * <p>
     * Reproductdion schema
     * </p>
     * Replaces the worst individuals with the generated by cross and mutation
     *
     * @param Variables             Structure of Variables
     */
    public void Reproduccion (TableVar Variables) {
        int pos;
        int[] indices = new int[long_poblacion];
        float[] fit = new float[long_poblacion];

        // Fist, copy individuals of intar in poblac
        for (int i=0;i<long_poblacion;i++)
            poblac.copyIndiv(i, Inter.getIndiv(i));

        // Save the fitness of the elements of inter
        for (int i=0; i<long_poblacion; i++) {
            fit[i]= poblac.getIndivFitness(i);
            indices[i] = i;
        }

        /* Sort the fitness array of actual population and store the values at "indices" */
        Utils.OrCrecIndex (fit,0,long_poblacion-1,indices);  /* To minimice */

        for (int i=0; i<descendientes; i++) {	/* For each descendant */
            pos = indices[long_poblacion-1-i];
            for (int j=0; j<Variables.getNVars(); j++) {
                if (RulesRep.equalsIgnoreCase("CAN"))   // Canonical representation of the rules
                    poblac.setCromElem(pos, j, Des.getCromElem(i,j));
                else     // DNF representation
                    for (int l=0; l<=Variables.getNLabelVar(j);l++)
                        poblac.setCromElemGene(pos, j, l, Des.getCromElemGene(i,j,l));
            }
            poblac.setIndivEvaluated(pos,false);

        }
        /* Remark: Always makes the substitution, without see if the new individual is better */

    }


    /**
     * <p>
     * Reproductdion schema - Modified steady step
     * </p>
     * Not used
     *
     * @param Variables             Structure of Variables
     */

    public void SteadyStepReproduction (TableVar Variables) {
        int pos;
        int[] indices = new int[long_poblacion];
        float[] fit = new float[long_poblacion];

        for (int i=0; i<long_poblacion; i++) {
            fit[i]= poblac.getIndivFitness(i);
            indices[i] = i;
        }

        // Sort the fitness array of actual population and store the values at "indices"
        Utils.OrDecIndex (fit,0,long_poblacion-1,indices);  // To maximise

        for (int i=0; i<descendientes; i++) {	// For each descendant
            pos = indices[long_poblacion-1-i];
            for (int j=0; j<Variables.getNVars(); j++) {
                if (RulesRep.equalsIgnoreCase("CAN"))   // Canonical representation of the rules
                    poblac.setCromElem(pos, j, Des.getCromElem(i,j));
                else {  // DNF representation
                    for (int k=0; k<=Variables.getNLabelVar(j); k++)
                        poblac.setCromElemGene(pos, j, k, Des.getCromElemGene(i,j,k));
                }

            }
            poblac.setIndivEvaluated(pos,false);
        }

    }

   

    /**
     * <p>
     * Composes the genetic algorithm applying the operators
     * </p>
     * @param Variables             Structure of Variables
     * @param Examples              Structure of Examples
     * @param NumObjectives         Number of objectives used
     * @param nFile                 File to write the process of the genetic algorithm
     */
    public void GeneticAlgorithm (TableVar Variables, TableDat Examples, int NumObjectives, String nFile) {
        int cruces;
        String contents;
        float porcVar = (float) 0.25;   // Percentaje of variables
        float porcPob = (float) 0.75;   // Percentaje of individuals


        /* Creates and initialices the population  */
        poblac = new Population (this, long_poblacion, Variables.getNVars(), RulesRep, Variables, Examples.getNEx(),NumObjectives);
        poblac.BsdInitPop (Variables, porcVar,porcPob);

        // Creates the elite population
        elite = new Population(this, long_elite, Variables.getNVars(), RulesRep, Variables, Examples.getNEx(),NumObjectives);

        // Echo of the lengths
        System.out.println ("Population length:" + long_poblacion);
        System.out.println ("Elite set length:" + long_elite);

        /* Creates the Temporal set (elite plus poblacion to compute fitness) */
        temporal = new Population (this, long_elite+long_poblacion, Variables.getNVars(), RulesRep, Variables, Examples.getNEx(),NumObjectives);
        used_temp  = 0;

        /* Creates the populations "Inter" and "Des" to aply the genetic operations */
        Inter = new Population(this, long_poblacion, Variables.getNVars(), RulesRep, Variables, Examples.getNEx(),NumObjectives);
        Des   = new Population(this, 2*long_poblacion, Variables.getNVars(), RulesRep, Variables, Examples.getNEx(),NumObjectives);

        /* Inicialization of variables */
        Trials = 0;
        Gen = 1;
        used_elite = 0;   // At the beginning, elite has 0 individuals


        do { /* GA General cycle */

            // Population will be evaluated once joined population and elite in temporal, and repetitions are eliminated

            // Joins population plus elite population into temporal population
            JoinTemp ();

            // Eliminate duplicates in temporal
            used_temp = temporal.delDup(used_temp);

            // Evaluate non evaluated individuals in poblac (without file writing)
            Trials += temporal.evalPop (this, used_temp, Variables, Examples);

            // Computes and stores the fitness of temporal individuals
            temporal.CalcFitness (used_temp, NumObjectives);

            // Compute the number of non dominated individuals
            int noDom=0;
            for (int x=0; x<used_temp; x++) {
                // If the individual x is not dominated, increase the number of non dominated
                if (!temporal.getIndivDom(x))
                    noDom++;
            }

            // Create the new elite population (with exactly "long_elite" individuals
            if (noDom<=long_elite)
                CompletaElite();
            else
                TruncaElite();

            noDom=0;
            for (int x=0; x<used_elite; x++) {
                // If the individual x is not dominated, increase the number of non dominated
                if (!elite.getIndivDom(x))
                    noDom++;
            }

            /* Binary Tournament Selection (from elite to Inter)  */
            BinTournSelect ();

            /* Crossover: Generated individuals are stored in Des */
            descendientes= 0;
            cruces = MultipointCrossover(Variables);
            descendientes = cruces*2;

            /* Mutation: new individuals stored in des */
            UniformBiasedMutation (Variables);

            /* Reproduction model: replaces worst individulas with the generated by cross and mutation */
            Reproduccion(Variables);

            /* Actual population evaluation (only evaluates new individuals not yet evaluated) */
            Trials+=poblac.evalPop (this, long_poblacion, Variables, Examples);

            /* Next generation  */
            Gen++;

        } while (Trials <= n_eval);   // GA finishes when the number of evaluations is reached


        /* SEG_File. GA concludes when the number of evaluations is reached */
        contents = "\nGenetic Algorithm execution finished\n";
        contents+= "\tNumber of Generations = " + (Gen-1) + "\n";
        contents+= "\tNumber of Evaluations = " + Trials + "\n";
        Files.addToFile(nFile, contents);
        


        /* Eliminate duplicates in elite population obtaining the remaining number of individuals */
        setUsedElite (elite.delDup(long_elite));

        /* Evaluation of individuals in elite (only evaluates new individuals not yet evaluated) */
        Trials+=elite.evalPop (this, used_elite, Variables, Examples);

    }


    
}
